package com.yox.rpc.transport.netty.server;

import com.yox.rpc.codec.CommonDecoder;
import com.yox.rpc.codec.CommonEncoder;
import com.yox.rpc.hook.ShutdownHook;
import com.yox.rpc.provider.ServiceProviderImpl;
import com.yox.rpc.registry.NacosServiceRegistry;
import com.yox.rpc.serializer.CommonSerializer;
import com.yox.rpc.transport.AbstractRpcServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

public class NettyServer extends AbstractRpcServer {

    private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);

    private final CommonSerializer serializer;

    public NettyServer(String host, int port) {
        this(host, port, DEFAULT_SERIALIZER);
    }
    public NettyServer(String host, int port, Integer serializerID) {
        this.host = host;
        this.port = port;
        this.serviceRegistry = new NacosServiceRegistry();
        this.serviceProvider = new ServiceProviderImpl();
        this.serializer = CommonSerializer.getByCode(serializerID);
        scanServices();
    }

    @Override
    public void start(){
        // 添加注销服务的钩子，服务端关闭的时候才会执行
        ShutdownHook.getShutdownHook().addClearAllHook();
        // 创建主线程池，对应 I/O 线程，主要用来处理新用户的连接事件
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        // 创建从线程池，主要用来处理已经连接用户的可读写事件
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // 初始化Netty服务端启动器，作为服务端入口
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            // 将主从"线程池"初始化到启动器中
            serverBootstrap.group(bossGroup, workerGroup)
                    // 设置服务端通道类型
                    .channel(NioServerSocketChannel.class)
                    // 日志打印方式
                    .handler(new LoggingHandler(LogLevel.INFO))
                    // 配置ServerChannel参数，服务端接收连接的最大队列长度，如果队列已满，客户端连接将被拒绝
                    .option(ChannelOption.SO_BACKLOG, 256)
                    // 启动该功能时，TCP会主动探测空闲连接的有效性。TCP的心跳机制，默认心跳间隔是2小时
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    // 配置Channel参数，nodelay没有延迟，true就代表禁用Nagle算法，减小传输延迟
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    // 初始化Handler，设置Handler操作
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 初始化管道
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            /**
                             * 往管道中添加Handler，注意入栈Handler与出栈Handler都必须按实际执行顺序添加，
                             * 比如先解码再Server处理，那Decoder()就必须放在NettyServerHandler()前面
                             * 但是入栈和出栈Handler之间互不影响，这里就是先添加都出栈，再添加都入栈
                             */
                            // 设定IdleStateHandler心跳检测每30秒进行一次读检测，如果30秒内ChannelRead()方法未被调用，则触发一次userEventTrigger()方法
                            pipeline.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS))
                                    .addLast(new CommonEncoder(serializer)) // 自定义编码器
                                    .addLast(new CommonDecoder()) // 自定义解码器
                                    .addLast(new NettyServerHandler()); // 服务端数据处理器
                        }
                    });

            // 绑定端口，阻塞等待绑定成功
            ChannelFuture future = serverBootstrap.bind(host, port).sync();

            // 等待服务器端监听端口关闭
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            logger.error("启动服务器时发生错误", e);
        } finally {
            logger.error("关闭 bossGroup 和 workerGroup ");
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}
